package com.miya.demo.common.util.excel;

import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.common.collect.Lists;
import com.miya.demo.common.enums.ErrorCodeEnum;
import com.miya.demo.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

/**
 * excel 工具类
 *
 * @author Caixiaowei
 * @date 2021/10/19
 */
@Slf4j
@Component
public class ExcelUtil {

	public static final Long PER_SHEET_ROW_COUNT = 10L;
	public static final Long PER_WRITE_ROW_COUNT = 5L;

	/**
	 * 导出Excel文件流,并写回Response
	 *
	 * @param data       数据
	 * @param titleClazz 标题clazz
	 */
	public static <T> void export(List<T> data, Class<T> titleClazz, String fileName) {
		if (data == null) {
			return;
		}
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		if (requestAttributes == null) {
			return;
		}
		HttpServletResponse response = ((ServletRequestAttributes) requestAttributes).getResponse();
		if (response == null) {
			return;
		}
		if (StrUtil.isEmpty(fileName)) {
			fileName = String.valueOf(System.currentTimeMillis());
		}
		try (ServletOutputStream outputStream = response.getOutputStream()) {
			fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");

			response.setCharacterEncoding("utf-8");
			response.setContentType("application/zip");
			response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
			EasyExcel.write(outputStream)
					.useDefaultStyle(false)
					.head(titleClazz)
					.sheet()
					.doWrite(data);
		} catch (IOException e) {
			log.error("Excel导出失败:{}", e.getMessage(), e);
			throw new BusinessException(ErrorCodeEnum.EXCEL_EXPORT_ERROR);
		}
	}

	/**
	 * 导出Excel文件流,并写回Response
	 *
	 * @param data       数据
	 * @param titleClazz 标题clazz
	 * @param response   响应
	 */
	public static <T> void export(List<T> data, Class<T> titleClazz, String fileName, HttpServletResponse response) {
		if (data == null) {
			return;
		}
		if (StrUtil.isEmpty(fileName)) {
			fileName = String.valueOf(System.currentTimeMillis());
		}
		try (ServletOutputStream outputStream = response.getOutputStream()) {
			fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");

			response.setCharacterEncoding("utf-8");
			response.setContentType("application/zip");
			response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
			EasyExcel.write(outputStream)
					.useDefaultStyle(false)
					.head(titleClazz)
					.sheet()
					.doWrite(data);
		} catch (IOException e) {
			log.error("Excel导出失败:{}", e.getMessage(), e);
			throw new BusinessException(ErrorCodeEnum.EXCEL_EXPORT_ERROR);
		}
	}

	/**
	 * 导出Excel文件流,并写回Response
	 *
	 * @param data       数据
	 * @param titleClazz 标题clazz
	 * @param response   响应
	 */
	public static <T> void exportSheet(List<T> data, Class<T> titleClazz, String fileName, String sheetName, HttpServletResponse response) {
		if (data == null) {
			return;
		}
		if (StrUtil.isEmpty(fileName)) {
			fileName = String.valueOf(System.currentTimeMillis());
		}
		try (ServletOutputStream outputStream = response.getOutputStream()) {
			fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");

			response.setCharacterEncoding("utf-8");
			response.setContentType("application/zip");
			response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
			ExcelWriter excelWriter = EasyExcel.write(outputStream, titleClazz).build();
			WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
			excelWriter.write(data, writeSheet);
			excelWriter.finish();
		} catch (IOException e) {
			log.error("Excel导出失败:{}", e.getMessage(), e);
			throw new BusinessException(ErrorCodeEnum.EXCEL_EXPORT_ERROR);
		}
	}

	/**
	 * 复杂的模板导出(web): 含有简单数据和列表数据, 回写到流
	 * 	会一次性将数据都加载到内存, 不适用大量数据
	 *
	 * @param data             简单数据
	 * @param list             列表数据
	 * @param response         响应
	 * @param templatePathName 模板路径名
	 * @param fileName         文件名称
	 */
	public static void exportByTemplate(Map<String, Object> data, List<Map<String, Object>> list, HttpServletResponse response,
										String templatePathName, String fileName) {
		ClassPathResource classPathResource = new ClassPathResource(templatePathName);
		ExcelWriter excelWriter = null;
		try {
			// 设置响应
			fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
			response.setCharacterEncoding("utf-8");
			response.setContentType("application/zip");
			response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);

			excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(classPathResource.getInputStream()).build();
			WriteSheet writeSheet = EasyExcel.writerSheet().build();
			FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();

			// 写入的数据
			excelWriter.fill(data, fillConfig, writeSheet);

			// 写入list 数据
			excelWriter.fill(new FillWrapper("clerk",list), fillConfig, writeSheet);

			excelWriter.finish();

		} catch (IOException e) {
			log.error("Excel 模板导出异常 {}", e);
			throw new RuntimeException(e);
		} finally {
			// 千万别忘记finish 会帮忙关闭流
			if (excelWriter != null) {
				excelWriter.finish();
			}
		}
	}

	/**
	 * 导出excel，多sheet导出
	 * @param tempFile tempFile
	 * @param data data
	 * @param clazzList clazzList
	 * @param sheetNameList sheetNameList
	 */
	public static void exportExcel(File tempFile, List<List<? extends BaseRowModel>> data, List<Class<? extends BaseRowModel>> clazzList, List<String> sheetNameList) {
		if (CollectionUtils.isEmpty(data)) {
			data = Lists.newArrayList();
		}

		ExcelWriter excelWriter = null;
		FileOutputStream fileOutputStream = null;
		try {
			fileOutputStream = new FileOutputStream(tempFile);
			excelWriter = new ExcelWriter(fileOutputStream, ExcelTypeEnum.XLSX);
			for (int i = 0; i < data.size(); i++) {
				ExcelWriterSheetBuilder excelWriterSheetBuilder = new ExcelWriterSheetBuilder(excelWriter);
				excelWriterSheetBuilder.registerWriteHandler(new ColumnWidthStyleStrategy());
				excelWriterSheetBuilder.registerWriteHandler(new HighlightCellWriteHandler());
				if (CollectionUtils.isNotEmpty(sheetNameList)) {
					excelWriterSheetBuilder.sheetName(sheetNameList.get(i));
				}
				excelWriterSheetBuilder.sheetNo(i);
				WriteSheet writeSheet = excelWriterSheetBuilder.build();
				writeSheet.setClazz(clazzList.get(i));
				excelWriter.write(data.get(i), excelWriterSheetBuilder.build());
			}
		} catch (Exception ex) {
			log.error("多sheet目录导出excel异常：", ex);
		} finally {
			if (excelWriter != null) {
				excelWriter.finish();
			}
			if(fileOutputStream != null) {
				try {
					fileOutputStream.close();
				} catch (IOException e) {
					log.warn("fileOutputStream.close error", e);
				}
			}
		}
	}
}
